<?php
namespace app\common\controller;
include app()->getRootPath() . "/vendor/WeChat/include.php";
include app()->getRootPath() ."/vendor/alipay/aop/AopCertClient.php";
include app()->getRootPath() ."/vendor/alipay/aop/request/AlipayTradeQueryRequest.php";
include app()->getRootPath() ."/vendor/alipay/aop/request/AlipayTradeAppPayRequest.php";
include app()->getRootPath() ."/vendor/alipay/aop/request/AlipayTradeRefundRequest.php";

class WxPay
{
    /**
     * 支付宝支付
     */
    public function alipays($param,$notify_url)
    {
        $aop = new \AopCertClient;
//        $appCertPath = config('config.alipay.appCertPath');
//        $alipayPublicKey = config('config.alipay.alipayPublicKey');
//        $rootCertPath = config('config.alipay.rootCertPath');
        $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
        $aop->appId = config('config.alipay.app_id');
        $aop->rsaPrivateKey = config('config.alipay.private_key');
//        $aop->alipayrsaPublicKey = $aop->getPublicKey($alipayPublicKey);//调用getPublicKey从支付宝公钥证书中提取公钥
        $aop->apiVersion = '1.0';
        $aop->signType = 'RSA2';
        $aop->postCharset='utf-8';
        $aop->format='json';
        $aop->isCheckAlipayPublicCert = false;//是否校验自动下载的支付宝公钥证书，如果开启校验要保证支付宝根证书在有效期内
//        $aop->isCheckAlipayPublicCert = true;//是否校验自动下载的支付宝公钥证书，如果开启校验要保证支付宝根证书在有效期内
//        $aop->appCertSN = $aop->getCertSN($appCertPath);//调用getCertSN获取证书序列号
//        $aop->alipayRootCertSN = $aop->getRootCertSN($rootCertPath);//调用getRootCertSN获取支付宝根证书序列号
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        $request = new \AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数
        $bizcontentarr = [
            'body' => '订单',
            'subject' => '订单支付',
            'out_trade_no' => $param['order_no'],
            'timeout_express' => '1d',//失效时间为 1天
            'total_amount' => $param['amount'],//价格
            'product_code' => 'QUICK_MSECURITY_PAY',
        ];

        $bizcontent = json_encode($bizcontentarr, JSON_UNESCAPED_UNICODE);

        //商户外网可以访问的异步地址 (异步回掉地址，根据自己需求写)
        $request->setNotifyUrl($notify_url);
        $request->setBizContent($bizcontent);

        //这里和普通的接口调用不同，使用的是sdkExecute
        $response = $aop->sdkExecute($request);
        return $response;
    }

    /**
     * 微信支付(app)
     */
    public function appPay($order_no,$notify_url,$amount)
    {
        $appid = config('config.wechat.appid');
        $app_secret = config('config.wechat.app_secret');
        $config = [
            'token'          => 'test',
            'appid'          => $appid,
            'appsecret'      => $app_secret,
            'encodingaeskey' => 'BJIUzE0gqlWy0GxfPp4J1oPTBmOrNDIGPNav1YFH5Z5',
            // 配置商户支付参数（可选，在使用支付功能时需要）
            'mch_id'         => config('config.wechat.mch_id'),
            'mch_key'        => config('config.wechat.mch_key'),
            // 配置商户支付双向证书目录（可选，在使用退款|打款|红包时需要）
            'ssl_key'        => '',
            'ssl_cer'        => '',
            // 缓存目录配置（可选，需拥有读写权限）
            'cache_path'     => '',
        ];
    

        // 创建接口实例
        $wechat = new \WeChat\Pay($config);
        // 组装参数，可以参考官方商户文档
        $options = [
            'body'             => 'pay',
            'out_trade_no'     => $order_no,
            'total_fee'        => $amount*100,
            'trade_type'       => 'APP',
            'notify_url'       => $notify_url,
            'spbill_create_ip' => '127.0.0.1',
        ];
        try {
            // 生成预支付码
            $result = $wechat->createOrder($options);
            $options = $wechat->createParamsForApp($result['prepay_id']);
            return ['code'=>0,'msg'=>'success','data'=>$options];
            // @todo 把 $options 传到前端用js发起支付就可以了
        } catch (\Exception $e) {
            // 出错啦，处理下吧
            return ['code'=>1,'msg'=>'发起支付失败'];
        }
    }

    /**
     * 微信支付(小程序)
     */
    public function pay($order_no,$openid,$notify_url,$amount)
    {
        $config = [
            'token'          => 'test',
            'appid'          => config('config.wechat.appid'),
            'appsecret'      => config('config.wechat.secret'),
            'encodingaeskey' => 'BJIUzE0gqlWy0GxfPp4J1oPTBmOrNDIGPNav1YFH5Z5',
            // 配置商户支付参数（可选，在使用支付功能时需要）
            'mch_id'         => config('config.wechat.mch_id'),
            'mch_key'        => config('config.wechat.mch_key'),
            // 配置商户支付双向证书目录（可选，在使用退款|打款|红包时需要）
            'ssl_key'        => '',
            'ssl_cer'        => '',
            // 缓存目录配置（可选，需拥有读写权限）
            'cache_path'     => '',
        ];
        // 创建接口实例
        $wechat = new \WeChat\Pay($config);
        // 组装参数，可以参考官方商户文档
        $options = [
            'body'             => 'pay',
            'out_trade_no'     => $order_no,
            'total_fee'        => $amount*100,
            'openid'           => $openid,
            'trade_type'       => 'JSAPI',
            'notify_url'       => $notify_url,
            'spbill_create_ip' => '127.0.0.1',
        ];

        try {
            // 生成预支付码
            $result = $wechat->createOrder($options);
            if (!empty($result['result_code']) && $result['result_code'] == 'FAIL') {
                return ['code'=>1,'msg'=>$result['err_code_des']];
            }
            // 创建JSAPI参数签名
            $options = $wechat->createParamsForJsApi($result['prepay_id']);
            return ['code'=>0,'msg'=>'success','data'=>$options];
            // @todo 把 $options 传到前端用js发起支付就可以了
        } catch (\Exception $e) {
            // 出错啦，处理下吧
            return ['code'=>1,'msg'=>'发起支付失败'];
        }
    }

    /**
     * 企业付款到零钱
     */
    public function backToUser($order_no,$openid,$out_detail_no,$amount)
    {
        $config = [
            'token'          => 'test',
            'appid'          => config('config.wechat.appid'),
            'appsecret'      => config('config.wechat.app_secret'),
            'encodingaeskey' => 'BJIUzE0gqlWy0GxfPp4J1oPTBmOrNDIGPNav1YFH5Z5',
            // 配置商户支付参数（可选，在使用支付功能时需要）
            'mch_id'         => config('config.wechat.mch_id'),
            'mch_key'        => config('config.wechat.mch_key'),
            // 配置商户支付双向证书目录（可选，在使用退款|打款|红包时需要）
            'ssl_key'        => config('config.wechat.ssl_key'),
            'ssl_cer'        => config('config.wechat.ssl_cer'),
            // 缓存目录配置（可选，需拥有读写权限）
            'cache_path'     => '',
        ];
        // 创建接口实例
        $wechat = new \WeChat\Pay($config);
        // 组装参数，可以参考官方商户文档
        $options = [
            'out_batch_no' => $order_no,
            'batch_name' => '微信提现',
            'batch_remark' => '微信提现',
            'total_amount' => $amount*100,
            'total_num' => $amount*100,
            'transfer_detail_list' => [
                'out_detail_no' => $out_detail_no,
                'transfer_amount' => $amount*100,
                'transfer_remark' => '提现',
                'openid' => $openid,
            ]
        ];

        try {
            $result = $wechat->createTransfers($options);
            if (!empty($result['result_code']) && $result['result_code'] == 'FAIL') {
                return ['code'=>1,'msg'=>$result['err_code_des']];
            }
            // 创建JSAPI参数签名
            $options = $wechat->createParamsForJsApi($result['prepay_id']);
            return ['code'=>0,'msg'=>'success','data'=>$options];
            // @todo 把 $options 传到前端用js发起支付就可以了
        } catch (\Exception $e) {
            // 出错啦，处理下吧
            return ['code'=>1,'msg'=>'提现失败,请联系开发人员'.$e->getMessage()];
        }
    }

    /**
     * 微信退款(小程序,app)
     */
    public function refund($insert,$notify_url,$pay_type)
    {
        if ($pay_type == 1) {
            $appid = config('config.wechat.app_appid');
            $app_secret = config('config.wechat.app_secret');
        } else {
            $appid = config('config.wechat.appid');
            $app_secret = config('config.wechat.secret');
        }
        $config = [
            'token'          => 'test',
            'appid'          => $appid,
            'appsecret'      => $app_secret,
            'encodingaeskey' => 'BJIUzE0gqlWy0GxfPp4J1oPTBmOrNDIGPNav1YFH5Z5',
            // 配置商户支付参数（可选，在使用支付功能时需要）
            'mch_id'         => config('config.wechat.mch_id'),
            'mch_key'        => config('config.wechat.mch_key'),
            // 配置商户支付双向证书目录（可选，在使用退款|打款|红包时需要）
            'ssl_key'        => config('config.wechat.ssl_key'),
            'ssl_cer'        => config('config.wechat.ssl_cer'),
            // 缓存目录配置（可选，需拥有读写权限）
            'cache_path'     => '',
        ];
        // 创建接口实例
        $wechat = new \WeChat\Pay($config);

        // 组装参数，可以参考官方商户文档
        $options = [
            'out_trade_no'     => $insert['order_no'],
            'out_refund_no'    => $insert['out_found_no'],
            'total_fee'        => $insert['amount']*100,
            'refund_fee'       => $insert['amount']*100,
            'notify_url'       => $notify_url,
            'spbill_create_ip' => '127.0.0.1',
        ];
        try {
            $result = $wechat->createRefund($options);
            return ['code'=>0,'msg'=>'success','data'=>$result];
        } catch (\Exception $e) {
            // 出错啦，处理下吧
            return ['code'=>1,'msg'=>'发起退款失败'];
        }
    }

    /**
     * 支付宝退款
     */
    public function alipaysBack($info)
    {
        $aop = new \AopCertClient();
        $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
        $aop->appId = config('config.alipay.app_id');
        $aop->rsaPrivateKey = config('config.alipay.private_key');
        $aop->ischeckAlipayPublicCert = true;
        $aop->apiVersion = '1.0';
        $aop->signType = 'RSA2';
        $aop->postCharset='UTF-8';
        $aop->format='json';
        $request = new \AlipayTradeRefundRequest();
        $bizcontentarr = [
            'out_trade_no' => $info['out_trade_no'],
            'refund_amount' => $info['refund_amount'],
            'refund_reason' => '订单取消-退款',
        ];
        $json = json_encode($bizcontentarr,JSON_UNESCAPED_UNICODE);
        $request->setBizContent($json);
        $result = $aop->execute ( $request);
        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if(!empty($resultCode)&&$resultCode == 10000){
            return ['code'=>0,'msg'=>'退款成功'];
        } else {
            return ['code'=>1,'msg'=>'退款失败'];
        }
    }

    /**
     *商家转账
     */
    public function zhuan($openid,$trade_no,$money,$desc){
        $params["mch_appid"]=config('config.wechat.appid');
        $params["mchid"] = config('config.wechat.mch_id');
        $params["nonce_str"]= 'suiji'.mt_rand(100,999);
        $params["partner_trade_no"] = $trade_no;
        $params["amount"]= $money;
        $params["desc"]= $desc;
        $params["openid"]= $openid;
        $params["check_name"]= 'NO_CHECK';
        $params['spbill_create_ip'] = $_SERVER['SERVER_ADDR'];
        $mch_key = config('config.wechat.mch_key');
        //生成签名
        $str = 'amount='.$params["amount"].'&check_name='.$params["check_name"].'&desc='.$params["desc"].'&mch_appid='.$params["mch_appid"].'&mchid='.$params["mchid"].'&nonce_str='.$params["nonce_str"].'&openid='.$params["openid"].'&partner_trade_no='.$params["partner_trade_no"].'&spbill_create_ip='.$params['spbill_create_ip'].'&key='.$mch_key;

        //md5加密 转换成大写
        $sign = strtoupper(md5($str));
        //生成签名
        $params['sign'] = $sign;

        //构造XML数据
        $xmldata = $this->array_to_xml($params); //数组转XML
        $url='https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';

        //发送post请求
        $res = $this->curl_post_ssl($url, $xmldata); //curl请求
        if(!$res){
            return array('status'=>1,
                'msg'=>"服务器连接失败" );
        }

        //付款结果分析
        $content = $this->xml_to_array($res); //xml转数组
        return $content;
    }

    /**
     * array 转 xml
     * 用于生成签名
     */
    public function array_to_xml($arr){
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" .$key.">".$val."</".$key.">";
            } else
                $xml .= "<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml .= "</xml>";
        return $xml;
    }

    /**
     * xml 转化为array
     */
    public function xml_to_array($xml){
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $values;
    }

    /**
     * curl请求
     */
    public function curl_post_ssl($url, $xmldata,  $second=30,$aHeader=array()){
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
        //这里设置代理，如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '10.206.30.98');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);

        //默认格式为PEM，可以注释
        curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
//绝对地址可使用 dirname(__DIR__)打印，如果不是绝对地址会报 58 错误
        curl_setopt($ch,CURLOPT_SSLCERT,config('config.wechat.ssl_cer'));
        curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLKEY,config('config.wechat.ssl_key'));
        if( count($aHeader) >= 1 ){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }
        curl_setopt($ch,CURLOPT_POST, 1);
        curl_setopt($ch,CURLOPT_POSTFIELDS,$xmldata);
        $data = curl_exec($ch);

        if($data){
            curl_close($ch);
            return $data;
        }
        else {
            $error = curl_errno($ch);
            echo "call faild, errorCode:$error\n";
            die();
            curl_close($ch);
            return false;
        }
    }
}